

#include<stdio.h>
#include<unistd.h>
#include<seccomp.h>
#include<sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/wait.h>
#include <sys/resource.h>

int sandbox_init(uint64_t exe_value, int allow_write_file)
{
#define DEBUG_LOG_INFO printf
     // 白名单
    int syscalls_whitelist[] = {SCMP_SYS(read), SCMP_SYS(fstat),
                                SCMP_SYS(mmap), SCMP_SYS(mprotect),
                                SCMP_SYS(munmap), SCMP_SYS(uname),
                                SCMP_SYS(arch_prctl), SCMP_SYS(brk),
                                SCMP_SYS(access), SCMP_SYS(exit_group),
                                SCMP_SYS(close), SCMP_SYS(readlink),
                                SCMP_SYS(sysinfo), SCMP_SYS(write),
                                SCMP_SYS(writev), SCMP_SYS(lseek),
                                SCMP_SYS(clock_gettime)};

    int syscalls_whitelist_length = sizeof(syscalls_whitelist) / sizeof(int);
    scmp_filter_ctx ctx = NULL;
    DEBUG_LOG_INFO("init \n");
    // 默认动作杀死进程
    ctx = seccomp_init(SCMP_ACT_KILL);
    if (!ctx) {
        return -1;
    }
    DEBUG_LOG_INFO("add \n");
    for (int i = 0; i < syscalls_whitelist_length; i++) {
        if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls_whitelist[i], 0) != 0) {
            return -1;
        }
    }
    // 这个表示地址相等
    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 1, SCMP_A0(SCMP_CMP_EQ, (scmp_datum_t)(exe_value))) != 0) {
        return -1;
    }
    if (!allow_write_file) {
        // do not allow "w" and "rw"
        if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1, SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, 0)) != 0) {
            return -1;
        }
        if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 1, SCMP_CMP(2, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, 0)) != 0) {
            return -1;
        }
    } else {
        if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0) != 0) {
            return -1;
        }
        if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup), 0) != 0) {
            return -1;
        }
        if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup2), 0) != 0) {
            return -1;
        }
        if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup3), 0) != 0) {
            return -1;
        }
    }
    DEBUG_LOG_INFO("load \n");
    if (seccomp_load(ctx) != 0) {
        DEBUG_LOG_INFO("load failed \n");
        return -1;
    }


    DEBUG_LOG_INFO("release \n");
    seccomp_release(ctx);
    return 0;
}


int child_process()
{
    char *exe_path = "/judger/run/8320dfa52f8c45b69917d0e038d9fd25/main";
    char *argv[] = {exe_path, NULL };
    char *envp[] = { 
                    "PATH=/usr/local/bin:/usr/bin:/bin",
                    "LANG=en_US.UTF-8", 
                    "LANGUAGE=en_US:en", 
                    "LC_ALL=en_US.UTF-8",
                    NULL};
    
    printf("exe_path %p; Arg[0] %p \n", exe_path, argv[0]);
    if(0 != sandbox_init((uint64_t)exe_path, 0))
    {
        printf("seccomp init failed  \n");
        return 0;
    }
    execve(exe_path, argv, envp);
    perror("execve"); 
}



int main()
{   
    printf("start...\n");

    pid_t child_pid = fork();
    if (child_pid < 0) {
        printf("fork failed \n");
        return 0;
    }
    else if (child_pid == 0) {
        child_process();       
    }
    else if (child_pid > 0){
        int status;
        struct rusage resource_usage;

        if (wait4(child_pid, &status, WSTOPPED, &resource_usage) == -1) {
            DEBUG_LOG_INFO("wait4 faild "); 
            return -1;       
        }
    }
    return 0;
}